package it.polimi.dei.SeCo;

import it.polimi.dei.SeCo.GeoNames.GeoNamesTool;
import it.polimi.dei.SeCo.LingPipe.MapDictionaryManager;
import it.polimi.dei.SeCo.LingPipe.LingPipeTool;
import it.polimi.dei.SeCo.YAGO.GeoEntity;
import it.polimi.dei.SeCo.YAGO.YAGOTool;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import org.geonames.Toponym;

import com.aliasi.chunk.Chunk;

import edu.smu.tspell.wordnet.SynsetType;

/**
 * @author Simone Benefico
 */
public class Main {	
	//WordNet parameter
	public final static String WORDNET_PATH = "C:\\Program Files (x86)\\WordNet\\2.1\\dict"; //"C:\\Program Files\\WordNet\\2.1\\dict"  "/opt/WordNet-3.0/dict/";
	
	//GeoNames parameter
	public final static String GEONAMES_USER = "simone.benefico";
	public final static GeoNamesTool gn = new GeoNamesTool(GEONAMES_USER);
	
	//LingPipe parameters
	private final static String POS_MODEL = "models/pos-en-general-brown.HiddenMarkovModel";
	private final static String NER_MODEL = "models/ne-en-qa-conll2003.AbstractCharLmRescoringChunker";
	
	//YAGO parameters
	private final static YAGOTool y = new YAGOTool("172.20.30.39", "yagoSmall", "iasella", "7b26Zrki", 5432);
	
	//dictionary parameters
	private final static String[] poiStrings = new String[]{"first-order administrative division", "second-order administrative division", "third-order administrative division", "fourth-order administrative division", "administrative division", "leased area", "political entity", "dependent political entity", "freely associated state", "independent political entity", "section of independent political entity", "semi-independent political entity", "parish", "territory", "zone", "buffer zone", "seaplane landing area", "anchorage", "bay", "bays", "bight(s)", "bank(s)", "stream bank", "section of bank", "bog(s)", "icecap", "channel", "lake channel(s)", "marine channel", "navigation channel", "confluence", "canal", "aqueduct", "canal bend", "drainage canal", "irrigation canal", "navigation canal(s)", "abandoned canal", "underground irrigation canal(s)", "section of canal", "cove(s)", "tidal creek(s)", "current", "cutoff", "dock(s)", "docking basin", "icecap dome", "icecap depression", "ditch", "drainage ditch", "irrigation ditch", "ditch mouth(s)", "estuary", "fishing area", "fjord", "fjords", "waterfall(s)", "section of waterfall(s)", "mud flat(s)", "tidal flat(s)", "glacier(s)", "gulf", "geyser", "harbor(s)", "section of harbor", "inlet", "former inlet", "lake bed(s)", "lagoon", "lagoons", "section of lagoon", "lake", "crater lake", "intermittent lake", "salt lake", "intermittent salt lake", "oxbow lake", "intermittent oxbow lake", "lakes", "underground lake", "crater lakes", "intermittent lakes", "salt lakes", "intermittent salt lakes", "section of lake", "salt evaporation ponds", "mangrove swamp", "moor(s)", "marsh(es)", "salt marsh", "narrows", "ocean", "overfalls", "pond", "intermittent pond", "salt pond", "intermittent salt pond(s)", "ponds", "fishponds", "intermittent ponds", "salt ponds", "pool(s)", "intermittent pool", "reach", "icecap ridge", "roadstead", "reef(s)", "coral reef(s)", "section of reef", "rapids", "reservoir(s)", "intermittent reservoir", "water tank", "ravine(s)", "sabkha(s)", "sound", "sea", "shoal(s)", "sill", "spring(s)", "sulphur spring(s)", "hot spring(s)", "stream", "anabranch", "stream bend", "canalized stream", "distributary(-ies)", "headwaters", "intermittent stream", "section of intermittent stream", "stream mouth(s)", "abandoned watercourse", "streams", "lost river", "section of stream", "strait", "swamp", "irrigation system", "canal tunnel", "wadi", "wadi bend", "wadi junction", "wadi mouth", "wadies", "section of wadi", "whirlpool", "well", "abandoned well", "wells", "wetland", "intermittent wetland", "watercourse", "waterhole(s)", "agricultural colony", "amusement park", "area", "drainage basin", "petroleum basin", "battlefield", "clearing", "common", "concession area", "coalfield", "continent", "coast", "business center", "housing development", "field(s)", "irrigated field(s)", "gasfield", "grazing area", "gravel area", "industrial area", "arctic land", "locality", "military base", "mining area", "maneuver area", "naval base", "oasis(-es)", "oilfield", "peat cutting area", "park", "port", "quicksand", "reserve", "agricultural reserve", "forest reserve", "hunting reserve", "nature reserve", "palm tree reserve", "reservation", "wildlife reserve", "region", "economic region", "lake region", "artillery range", "salt area", "snowfield", "tribal area", "populated place", "seat of a first-order administrative division", "seat of a second-order administrative division", "seat of a third-order administrative division", "seat of a fourth-order administrative division", "capital of a political entity", "farm village", "seat of government of a political entity", "populated locality", "abandoned populated place", "religious populated place", "populated places", "destroyed populated place", "section of populated place", "israeli settlement", "causeway", "oil pipeline", "promenade", "portage", "road", "ancient road", "road bend", "road cut", "road junction", "railroad junction", "railroad", "abandoned railroad", "caravan route", "railroad yard", "street", "stock route", "tunnel", "natural tunnel", "road tunnel", "railroad tunnel", "tunnels", "trail", "administrative facility", "agricultural facility", "airbase", "airfield", "heliport", "airport", "abandoned airfield", "amphitheater", "ancient site", "aquaculture facility", "arch", "astronomical station", "asylum", "athletic field", "automatic teller machine", "bank", "beacon", "bridge", "ruined bridge", "building(s)", "office building", "boundary marker", "barracks", "breakwater", "baling station", "boatyard", "burial cave(s)", "bus station", "bus stop", "cairn", "cave(s)", "church", "camp(s)", "logging camp", "labor camp", "mining camp", "oil camp", "abandoned camp", "refugee camp", "cemetery", "communication center", "corral(s)", "casino", "castle", "customs house", "courthouse", "atomic center", "community center", "facility center", "medical center", "religious center", "space center", "convent", "dam", "ruined dam", "sub-surface dam", "dairy", "dry dock", "dockyard", "dike", "diplomatic facility", "fuel depot", "estate(s)", "oil palm plantation", "rubber plantation", "sugar plantation", "tea plantation", "section of estate", "facility", "foundry", "farm", "abandoned farm", "farms", "farmstead", "fort", "ferry", "gate", "garden(s)", "ghat", "guest house", "gas-oil separator plant", "local government office", "grave", "hermitage", "halting place", "house(s)", "country house", "hospital", "clinic", "dispensary", "leprosarium", "historical site", "hotel", "hut", "huts", "military installation", "research institute", "jetty", "landing", "leper colony", "library", "landfill", "lock(s)", "lighthouse", "mall", "marina", "factory", "brewery", "cannery", "copper works", "limekiln", "munitions plant", "phosphate works", "abandoned factory", "sugar refinery", "market", "mill(s)", "ore treatment plant", "olive oil mill", "sugar mill", "former sugar mill", "sawmill", "windmill", "water mill", "mine(s)", "gold mine(s)", "coal mine(s)", "chrome mine(s)", "copper mine(s)", "iron mine(s)", "monument", "salt mine(s)", "abandoned mine", "quarry(-ies)", "mole", "mosque", "mission", "abandoned mission", "monastery", "metro station", "museum", "novitiate", "nursery(-ies)", "observation point", "observatory", "radio observatory", "oil pipeline junction", "abandoned oil well", "oil refinery", "tank farm", "oil well", "opera house", "palace", "pagoda", "pier", "parking lot", "oil pumping station", "water pumping station", "post office", "police post", "abandoned police post", "park gate", "park headquarters", "prison", "reformatory", "abandoned prison", "power station", "hydroelectric power station", "border post", "customs post", "patrol post", "pyramid", "pyramids", "quay", "traffic circle", "golf course", "racetrack", "restaurant", "store", "resthouse", "rookery", "religious site", "retreat", "ranch(es)", "railroad siding", "railroad signal", "resort", "railroad station", "abandoned railroad station", "railroad stop", "abandoned railroad stop", "ruin(s)", "school", "agricultural school", "college", "language school", "military school", "maritime school", "technical school", "State Exam Prep Centre", "sheepfold", "shrine", "storehouse", "sluice", "sanatorium", "spa", "spillway", "square", "stable", "stadium", "scientific research base", "coast guard station", "experiment station", "forest station", "inspection station", "meteorological station", "radio station", "satellite station", "whaling station", "steps", "sewage treatment plant", "theater", "tomb(s)", "temple(s)", "cattle dipping tank", "tower", "transit terminal", "triangulation station", "oil pipeline terminal", "temp work office", "university prep school", "university", "united states government establishment", "veterinary facility", "wall", "ancient wall", "weir(s)", "wharf(-ves)", "wreck", "waterworks", "free trade zone", "zoo", "asphalt lake", "atoll(s)", "bar", "beach", "beaches", "badlands", "boulder field", "blowhole(s)", "blowout(s)", "bench", "butte(s)", "cape", "cleft(s)", "caldera", "cliff(s)", "canyon", "cone(s)", "corridor", "cirque", "cirques", "crater(s)", "cuesta(s)", "delta", "depression(s)", "desert", "dune(s)", "divide", "sandy desert", "fan(s)", "ford", "fissure", "gap", "gorge(s)", "headland", "hill", "hills", "hammock(s)", "rock desert", "interfluve", "island", "islet", "artificial island", "mangrove island", "islands", "land-tied island", "section of island", "isthmus", "karst area", "lava area", "levee", "mesa(s)", "mound(s)", "moraine", "mountain", "mountains", "meander neck", "nunatak", "nunataks", "pan", "pans", "pass", "peninsula", "section of peninsula", "peak", "peaks", "plateau", "section of plateau", "polder", "plain(s)", "section of plain", "promontory(-ies)", "point", "points", "beach ridge", "ridge(s)", "stony desert", "rock", "rockfall", "rocks", "sand area", "dry stream bed", "escarpment", "saddle", "shore", "sinkhole", "slide", "slope(s)", "spit", "spur(s)", "talus slope", "interdune trough(s)", "terrace", "upland", "valley", "hanging valley", "valleys", "section of valley", "volcano", "apron", "arch", "arrugado", "borderland", "banks", "bank", "basin", "cordillera", "canyons", "canyon", "continental rise", "deep", "shelf edge", "escarpment", "scarp", "fan", "flat", "fracture zone", "furrow", "gap", "gully", "hill", "hills", "hole", "knoll", "knolls", "ledge", "levee", "mesa", "mound", "moat", "mountain", "peaks", "peak", "plain", "plateau", "pinnacle", "province", "ridge", "ridges", "reefs", "reef", "rise", "seachannel", "seachannels", "saddle", "shelf", "shoal", "shoals", "shelf valley", "sill", "slope", "seamounts", "seamount", "spur", "terrace", "tablemounts", "guyots", "tablemount", "guyot", "tongue", "trough", "trench", "valley", "valleys", "bush(es)", "cultivated area", "forest(s)", "fossilized forest", "grassland", "coconut grove", "olive grove", "palm grove", "pine grove", "heath", "meadow", "orchard(s)", "scrubland", "tree(s)", "tundra", "vineyard", "vineyards"};
	private final static MapDictionaryManager poiDictionary = new MapDictionaryManager(poiStrings, "PLACE", false, false, false, SynsetType.NOUN);
	private final static String[] rbsOfPlaceStrings = new String[]{"above", "abroad", "away", "back", "behind", "below", "down", "downstairs", "east", "west", "north", "south", "elsewhere", "far", "here", "in", "indoors", "inside", "near", "nearby", "off", "on", "out", "outside", "over", "there", "towards", "under", "up", "upstairs", "where", "backwards", "downwards", "eastwards", "forwards", "homewards", "upwards", "anywhere", "everywhere", "nowhere", "somewhere"};
	private final static MapDictionaryManager rbsOfPlaceDictionary = new MapDictionaryManager(rbsOfPlaceStrings, "ADVERB OF PLACE", true, false, false, SynsetType.ADVERB);
	
	/**
	 * Starting point and flow manager for the application.
	 * @throws IOException 
	 */
	public static void main(String[] args) throws IOException {
		System.out.print("Query: \t");
		BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
		String text = in.readLine();
		
		List<Chunk> sentences = LingPipeTool.detectSentences(text);		
		
		Map<Chunk, Map<Chunk, List<GeoEntity>>> allLocationsMap = new HashMap<Chunk, Map<Chunk, List<GeoEntity>>>();
		
		int i=1;
		for(Chunk sentenceChunk : sentences) {
			Map<Chunk, List<GeoEntity>> locationsMap = new HashMap<Chunk, List<GeoEntity>>();
			String sentence = text.substring(sentenceChunk.start(),sentenceChunk.end());
			if(sentence.endsWith("."))
				sentence = sentence.substring(0, sentence.length()-1);
			if(sentence.endsWith("?") && !sentence.endsWith(" ?"))
				sentence = sentence.substring(0, sentence.length()-1) + " ?";
			System.out.println("***SENTENCE "+ (i++) + "***\n" + sentence);
			
			List<Chunk> names = new ArrayList<Chunk>();
					
			//LingPipe - Statistical NameFinder
			names = LingPipeTool.findNames(sentence, NER_MODEL);
			List<Chunk> locations = LingPipeTool.filterLocations(names);
			//List<Chunk> organizations = LingPipeTool.filterOrganizations(names);
			
			List<String> locationStrings = LingPipeTool.chunkListStrings(sentence, locations);
			if(locations!=null && !locations.isEmpty()) {
				System.out.println("\nLOCATIONS: ");
				printChunks(locations, locationStrings);
			}			

			/*List<String> organizationStrings = LingPipeTool.chunkListStrings(sentence, organizations);
			if(organizations!=null && !organizations.isEmpty()) {
				System.out.println("\nORGANIZATIONS: ");
				printChunks(organizations, organizationStrings);
			}*/
			
			List<Chunk> phraseChunks = LingPipeTool.phraseChunk(sentence, POS_MODEL);
			
			//LingPipe - Dictionary-Based NameFinder
			List<Chunk> pois = LingPipeTool.getPhraseChunks(LingPipeTool.removeAlreadyPicked(LingPipeTool.find(sentence, poiDictionary.getDictionary()), names),phraseChunks);
			List<String> poisStrings = LingPipeTool.chunkListStrings(sentence, pois);
			if(pois!=null && !pois.isEmpty()) {
				System.out.println("\nPLACES: ");
				printChunks(pois, poisStrings);
			}
			if(pois!=null && !pois.isEmpty()) {
				if(names==null)
					names = new ArrayList<Chunk>();
				names.addAll(pois);
			}
			
			//LingPipe - Part-of-Speech Tagger			
			List<Chunk> tokenChunks = LingPipeTool.tokenChunk(sentence);
			
			Map<Chunk, String> tagsMap = LingPipeTool.tagPOS(sentence, POS_MODEL);
			
			List<Chunk> adverbsOfPlace = LingPipeTool.find(sentence, rbsOfPlaceDictionary.getDictionary());
			
			List<Chunk> phraseChunksOfNames = LingPipeTool.getPhraseChunks(names, phraseChunks);
			List<Chunk> candidateWays = LingPipeTool.removeType(LingPipeTool.removeAlreadyPicked(phraseChunks, phraseChunksOfNames), "verb");
			Map<Chunk, String> waysMap = LingPipeTool.getPhraseChunks(sentence, adverbsOfPlace, candidateWays, tokenChunks, LingPipeTool.getAdverbs(tagsMap));
			if(waysMap!=null && !waysMap.isEmpty()) {
				List<Chunk> chunksOfWays = new ArrayList<Chunk>(waysMap.keySet());
				List<String> chunksOfWaysStrings = LingPipeTool.chunkListStrings(sentence, chunksOfWays);
				if(adverbsOfPlace!=null && !adverbsOfPlace.isEmpty()) {
					System.out.println("\nWAYS TO REFER TO LOCATIONS AND PLACES: ");
					if(chunksOfWays!=null && !chunksOfWays.isEmpty())
						printMapOfChunks(waysMap, chunksOfWaysStrings);
				}
			}
			
			//YAGO2
			if(locations!=null && !locations.isEmpty())	 {		
				for(Chunk location : locations) {
					String name = sentence.substring(location.start(), location.end());
					List<GeoEntity> geoEntities = new ArrayList<GeoEntity>();
					GeoEntity geoEntity = y.getGeoEntity(name);
					if(geoEntity != null) {
						geoEntities.add(geoEntity);
						locationsMap.put(location, geoEntities);
					}
					else {
						List<String> meanings = y.getSignificantMeanings(name);
						if(meanings!=null && !meanings.isEmpty()) {
							for(String meaning : meanings) {
								geoEntity = y.getGeoEntity(meaning);
								if(geoEntity!=null)
									geoEntities.add(geoEntity);
							}
							locationsMap.put(location, geoEntities);
						}
					}
				}
							
				//GeoNames - Search			
				Toponym toponym = null;
				for(Chunk location: locations) {
					List<GeoEntity> geoEntities = new ArrayList<GeoEntity>();
					if(!locationsMap.containsKey(location)) {
						String name = sentence.substring(location.start(), location.end());
						toponym = gn.getBestResult(gn.search(name), name);
						if(location!=null){
							geoEntities.add(gn.toGeoEntity(toponym));
							locationsMap.put(location, geoEntities);
						}
					}
				}
				
				allLocationsMap.put(sentenceChunk, locationsMap);
						
				List<String> entityNames = LingPipeTool.chunkListStrings(sentence, new ArrayList<Chunk>(locationsMap.keySet()));
				if(locationsMap!=null && !locationsMap.isEmpty()) {
					System.out.println("\nGEO ENTITIES:");
					printMapOfLocations(locationsMap, entityNames);
				}
			}
			
			System.out.println("\n------------------------------------------\n");
		}
		
		System.out.println("All locations: "+allLocationsMap.toString());
	}
	
	/**
	 * Prints a list of Chunks with the corresponding Strings.<br>
	 * It is used to print the NEs found by both statistical and dictionary-based NERs.
	 * @param chunks list of Chunks to print
	 * @param strings list of the corresponding Strings to print
	 */
	public static void printChunks(List<Chunk> chunks, List<String> strings) {
		Iterator<Chunk> chunksIterator = chunks.iterator();
		Iterator<String> stringsIterator = strings.iterator();
		while(chunksIterator.hasNext())
			System.out.println(chunksIterator.next() + " --> " + stringsIterator.next());
	}
	
	/**
	 * Prints the entries<Chunk, String> of a map with the corresponding Strings.<br>
	 * It is used to print the ways to refer to locations.
	 * @param map map of entries<Chunk, String> to print
	 * @param strings list of the corresponding Strings to print
	 */
	public static void printMapOfChunks(Map<Chunk, String> map, List<String> strings) {
		Iterator<Entry<Chunk, String>> itr = map.entrySet().iterator();
		Iterator<String> itrStrings = strings.iterator();
		Entry<Chunk, String> tmpEntry = null;
		Chunk tmpChunk = null;
		String tmpString = null;		
		while(itr.hasNext()) {
			tmpEntry = itr.next();
			tmpChunk = tmpEntry.getKey();
			tmpString = tmpEntry.getValue();
			System.out.println(tmpChunk.start()+"-"+tmpChunk.end()+":"+tmpString+" --> "+itrStrings.next());
		}
	}
	
	/**
	 * Prints the entries<Chunk, List<GeoEntity>> of a map with the corresponding Strings.<br>
	 * It is used to print all the GeoEntities found in all sentences.
	 * @param map map of entries<Chunk, List<GeoEntity>> to print
	 * @param strings list of the corresponding Strings to print
	 */
	public static void printMapOfLocations(Map<Chunk, List<GeoEntity>> map, List<String> strings) {
		Iterator<Entry<Chunk, List<GeoEntity>>> entriesIterator = map.entrySet().iterator();
		Iterator<String> stringsIterator = strings.iterator();
		Entry<Chunk, List<GeoEntity>> entry = null;
		while(entriesIterator.hasNext()) {
			entry = entriesIterator.next();
			System.out.println(entry.getKey() + " --> " + stringsIterator.next() + ":");
			List<GeoEntity> values = entry.getValue();
			if(values!=null && !values.isEmpty())
				for(GeoEntity entity : values)
					if(entity!=null)
						System.out.println(" * "+entity.toString());
		}
	}
}
